
package org.opencms.jsp;

import org.opencms.file.CmsObject;
import org.opencms.flex.CmsFlexController;
import org.opencms.jsp.util.CmsJspImageBean;
import org.opencms.loader.CmsImageScaler;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.staticexport.CmsLinkManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.servlet.ServletRequest;
import javax.servlet.jsp.JspException;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;

/**
 * This tag allows using the OpenCms native image scaling mechanism within JSP.<p>
 *
 * <em>No output is generated by this tag!</em>
 * Instead the tag generates a {@link org.opencms.jsp.util.CmsJspImageBean}.
 * This can be used to further process the selected image
 * with the provided image scaling parameters.<p>
 *
 * The following image formats are supported: BMP, GIF, JPEG, PNG, PNM, TIFF.<p>
 *
 * <em>
 * Note: Picture scaling is by default only enabled for target size with width and height
 * &lt;=1500. The size can be changed in the image scaler configuration in the file
 * <code>opencms-vfs.xml</code> in the body of the tag <code>&lt;loader&gt;</code>. Also other
 * options for the image scaler are set there.
 * </em>
 * <p>
 * This tag is an alternative to the OpenCms standard tag cms:img, providing additional flexibility.
 * This way you can use scaled images for:
 * <ul>
 *   <li>The standard HTML &lt;img&gt;-Tag.</li>
 *   <li>
 *     The HTML 5 &lt;picture&gt;-Tag with multiple sources (hi-DPI variants for retina displays)
 *     for responsive design.
 *   </li>
 *   <li>Further processing a scaled image anyway you want on your JSP.</li>
 * </ul>
 * </em>
 *
 * <p>
 * Example for a simple JSP that uses this tag to obtain information about an image:
 * <code>
 * &lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;<br>
 * &lt;%@ taglib prefix=&quot;cms&quot; uri=&quot;http://www.opencms.org/taglib/cms&quot;%&gt;<br>
 *<br>
 * &lt;cms:scaleImage var=&quot;imgBean&quot; src=&quot;/.galleries/samples/09.jpg?__scale=h:385,w:961,cx:42,cy:32,ch:385,cw:961&quot; /&gt;<br>
 * srcUrl: &lt;c:out value=&quot;${imgBean.srcUrl}&quot; /&gt;&lt;br&gt;<br>
 * vfsUri: &lt;c:out value=&quot;${imgBean.vfsUri}&quot; /&gt;&lt;br&gt;<br>
 * scalerParams: &lt;c:out value=&quot;${imgBean.scaler.requestParam}&quot; /&gt;&lt;br&gt;<br>
 * Width: &lt;c:out value=&quot;${imgBean.width}&quot; /&gt;&lt;br&gt;<br>
 * Height: &lt;c:out value=&quot;${imgBean.height}&quot; /&gt;&lt;br&gt;<br>
 * scaledWidth: &lt;c:out value=&quot;${imgBean.scaler.width}&quot; /&gt;&lt;br&gt;<br>
 * scaledHeight: &lt;c:out value=&quot;${imgBean.scaler.height}&quot; /&gt;&lt;br&gt;<br>
 * isScaled: &lt;c:out value=&quot;${imgBean.scaled}&quot; /&gt;&lt;br&gt;<br>
 * </code>
 */
public class CmsJspTagScaleImage extends CmsJspImageScalerTagSupport {

    /** The log object for this class. */
    private static final Log LOG = CmsLog.getLog(CmsJspTagScaleImage.class);

    /** Serial version UID required for safe serialization. */
    private static final long serialVersionUID = -6639978110802734737L;

    /** List of hi-DPI variant sizes to produce, e.g. 1.3x, 1.5x, 2x, 3x */
    private List<String> m_hiDpiVariantList;

    /** Name of the request attribute used to store the created image bean. */
    private String m_var;

    /**
     * Creates a new image scaling tag instance.<p>
     */
    public CmsJspTagScaleImage() {
        super();
    }

    /**
     * Internal action method to create the scaled image bean.<p>
     *
     * @param cms the cms context
     * @param imageUri the image URI
     * @param targetScaler the target image scaler
     * @param hiDpiVariantList  optional list of hi-DPI variant sizes to produce, e.g. 1.3x, 1.5x, 2x, 3x
     *
     * @return the created ScaledImageBean bean
     *
     * @throws CmsException in case something goes wrong
     */
    public static CmsJspImageBean imageTagAction(
        CmsObject cms,
        String imageUri,
        CmsImageScaler targetScaler,
        List<String> hiDpiVariantList)
    throws CmsException {

        CmsJspImageBean image = new CmsJspImageBean(cms, imageUri, targetScaler);

        // now handle (preset) hi-DPI variants
        if ((hiDpiVariantList != null) && (hiDpiVariantList.size() > 0)) {
            for (String hiDpiVariant : hiDpiVariantList) {

                CmsJspImageBean hiDpiVersion = image.createHiDpiVariation(hiDpiVariant);

                if (hiDpiVersion != null) {
                    image.addHiDpiImage(hiDpiVariant, hiDpiVersion);
                }
            }
        }
        return image;
    }

    /**
     * Does some cleanup before returning EVAL_PAGE
     *
     * @see javax.servlet.jsp.tagext.Tag#doEndTag()
     */
    @SuppressWarnings("unused")
    @Override
    public int doEndTag() throws JspException {

        release();
        return EVAL_PAGE;
    }

    /**
     * Handles the Start tag, checks some parameters, uses the CmsImageScaler to create a scaled
     * version of the image (and hi-DPI variants if necessary), stores all information in a
     * image bean and stores it as a request attribute (the name for this attribute is given
     * with the tag attribute "var").
     *
     * @return EVAL_BODY_INCLUDE or SKIP_BODY in case of an unexpected Exception (please consult
     * the OpenCms log file if that happens)
     */
    @Override
    public int doStartTag() {

        ServletRequest req = pageContext.getRequest();

        // this will always be true if the page is called through OpenCms
        if (CmsFlexController.isCmsRequest(req)) {

            try {
                CmsJspImageBean scaledImage = null;
                try {
                    CmsFlexController controller = CmsFlexController.getController(req);
                    CmsObject cms = controller.getCmsObject();
                    String src = CmsLinkManager.getAbsoluteUri(m_src, controller.getCurrentRequest().getElementUri());
                    scaledImage = imageTagAction(cms, src, m_scaler, m_hiDpiVariantList);
                } catch (CmsException e) {
                    // any issue accessing the VFS - just return SKIP_BODY
                    // otherwise template layout will get mixed up with nasty exception messages
                    if (LOG.isWarnEnabled()) {
                        LOG.warn(Messages.get().getBundle().key(Messages.ERR_IMAGE_TAG_VFS_ACCESS_1, m_src), e);
                    }
                }
                pageContext.getRequest().setAttribute(m_var, scaledImage);
            } catch (Exception ex) {
                if (LOG.isErrorEnabled()) {
                    LOG.error(Messages.get().getBundle().key(Messages.ERR_PROCESS_TAG_1, "scaleImage"), ex);
                }
                return SKIP_BODY;
            }
        }
        return EVAL_BODY_INCLUDE;
    }

    /**
     * Does some cleanup before the tag is released to the tag pool
     *
     * @see javax.servlet.jsp.tagext.Tag#release()
     */
    @Override
    public void release() {

        m_hiDpiVariantList = null;
        m_var = null;
        super.release();
    }

    /**
     * Sets the String containing a comma separated list of hi-DPI variants to produce line "1.3x,1.5x,2x,3x".<p>
     *
     * Currently in most cases "2x" should suffice to generate an additiona image for retina screens.
     *
     * Please note that since 11.0 the variants are created by lazy initialization, so there is usually no
     * need to use this.<p>
     *
     * @param value comma separated list of hi-DPI variants to produce, e.g. "1.3x,1.5x,2x,3x"
     */
    public void setHiDpiVariants(String value) {

        m_hiDpiVariantList = new ArrayList<>(4);
        String[] multipliers = StringUtils.split(value, ',');
        Collections.addAll(m_hiDpiVariantList, multipliers);
    }

    /**
     * Sets the name of the variable used for storing the resulting bean.
     *
     * @param value name of the resulting CmsJspScaledImage bean
     */
    public void setVar(String value) {

        m_var = value;
    }
}
